<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>1. 如何理解前端和 Vue | 被删的前端游乐场</title>
    <meta name="generator" content="VuePress 1.8.2">
    
    <meta name="description" content="Just playing around">
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.1e2670bf.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.38d016d1.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.e3f029cb.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/140.f7dc7755.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link router-link-active">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link router-link-active">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>9102 全员学 Vue</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html" aria-current="page" class="active sidebar-link">1. 如何理解前端和 Vue</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#页面组成" class="sidebar-link">页面组成</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#页面渲染" class="sidebar-link">页面渲染</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#事件驱动" class="sidebar-link">事件驱动</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#前端框架的出现" class="sidebar-link">前端框架的出现</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#数据驱动" class="sidebar-link">数据驱动</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#前端工程化" class="sidebar-link">前端工程化</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html#结束语" class="sidebar-link">结束语</a></li></ul></li><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html" class="sidebar-link">2. 怎么三两下拼出一个页面</a></li><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html" class="sidebar-link">3. 把页面拼成个Web应用</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>深入理解 Vue 动画效果</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>Webpack 实现 Vue 多页应用</span> <span class="arrow right"></span></p> <!----></section></li><li><a href="http://www.godbasin.com/vue-ebook/" target="_blank" rel="noopener noreferrer" class="sidebar-link">深入理解Vue.js与实战<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>最近身边学习 Vue 的人也越来越多了，大家都有很多的疑问，为什么前端要用框架、Vue 做了些什么、要怎么上手，等等。距离上一次写 Vue 也过去三年了，是时候再把这三年自己的理解再整理进去了。由于这个系列主要是希望写给可能前端掌握也不深的人，所以会铺垫一些自己认为需要的内容，本节先来讲讲入门前端，作为铺垫，来理解 Vue 这个框架吧。</p> <p>最近根据之前收集大家的一些对前端和 Vue 的疑问，整理了个科普文章，内容包括：</p> <ol><li>HTML/CSS/JS 职责，浏览器如何解析</li> <li>浏览器是怎么渲染页面的</li> <li>Vue 这类框架都做了什么（前端模版引擎简介）</li> <li>Vue 与传统 jQuery 区别（理解数据驱动与事件驱动）</li> <li>前端工程化的简单介绍</li></ol> <h1 id="前端页面解析"><a href="#前端页面解析" class="header-anchor">#</a> 前端页面解析</h1> <h2 id="页面组成"><a href="#页面组成" class="header-anchor">#</a> 页面组成</h2> <p>我们打开一个前端项目，经常会看到很多不同后缀的文件，例如一个页面可能包括<code>a.html</code>、<code>a.css</code>、<code>a.js</code>，用了 Vue 还有<code>a.vue</code>，再加上 Typescript 可能还有<code>a.ts</code>。</p> <p>相信有些没写过前端的开发们是有点崩溃的，我们先来分别看看一个前端页面都是由什么组成的。其实<strong>最终跑在浏览器中的代码，主要包括三种：HTML、CSS、Javascript。</strong></p> <h3 id="html"><a href="#html" class="header-anchor">#</a> HTML</h3> <p>直接从代码说起，最简单的莫过于：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">&gt;</span></span>我的第一个标题<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>我的第一个段落。<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>这里面包括两个子模快：</p> <ul><li><code>&lt;head&gt;</code>：常包括控制样式的<code>&lt;link&gt;</code>标签、控制浏览器特殊逻辑的<code>&lt;meta&gt;</code>标签、控制代码执行逻辑的<code>&lt;script&gt;</code>，不展示到页面。</li> <li><code>&lt;body&gt;</code>：包括展示在页面的内容。</li></ul> <p>通常来说，一段 HTML 代码，最终在浏览器中会生成一堆 DOM 节点树，例如：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span><span class="token punctuation">&gt;</span></span>123<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>456<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>789<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>这段代码在浏览器中渲染时，其实是长这个样子的：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-3.png" alt=""></p> <p>这不一定是最终的结果，因为我们还可以这样操作：</p> <ol><li>通过 CSS 样式，例如<code>display: none</code>来让其中某个节点消失。</li> <li>通过 JS 代码操作 DOM，例如使用<code>document.getElementById</code>获取到某个节点元素，然后再通过设置<code>.innerHTML</code>来改变元素的内容。</li></ol> <h3 id="css"><a href="#css" class="header-anchor">#</a> CSS</h3> <p>CSS 主要是给我们的 HTML 元素添加样式，可以通过几个方式匹配:</p> <ul><li>DOM 元素匹配：例如<code>p{color: red}</code>会让所有<code>&lt;p&gt;</code>元素的文字都变成红色</li> <li>class 匹配：类的匹配，例如<code>.color-red{color: red}</code>会让所有<code>&lt;xxx class=&quot;color-red&quot;&gt;</code>元素的文字都变成红色</li> <li>id 匹配：id 标识符的匹配，例如<code>#color-red{color: red}</code>会让<code>&lt;xxx id=&quot;color-red&quot;&gt;</code>元素的文字都变成红色（若页面内有多个相同的 id，则只有第一个生效）</li></ul> <p>CSS 的调试和编写上手不简单，会需要很多的踩坑和练习，它也不像 JS 那样可以完全根据语法或是逻辑理解，总有兼容性会颠覆你之前的认知。不过 CSS 写好了，对写页面的效率会有很大的提升。</p> <h3 id="javascript"><a href="#javascript" class="header-anchor">#</a> Javascript</h3> <p>HTML 是简单的网页静态信息，而 JavaScript 可以在网页上实现复杂的功能。</p> <p>我们常常使用 Javascript 来做以下事情：</p> <ul><li>处理事件（点击、输入等）</li> <li>改变 HTML 内容、位置和样式</li> <li>处理 Http 请求、各种业务逻辑的执行</li> <li>很多其他的事情也可以做</li></ul> <p>Javascript 是单线程的，更多是因为对页面交互的同步处理。作为浏览器脚本语言，JavaScript 的主要用途是与用户互动，以及操作 DOM，若是多线程会导致严重的同步问题。</p> <p>关于更多的 Javascript，可以阅读<a href="/front-end-playground/front-end-basic/front-end/front-end-5.html">《5. Javascript》</a>。</p> <h2 id="页面渲染"><a href="#页面渲染" class="header-anchor">#</a> 页面渲染</h2> <h3 id="浏览器的渲染机制"><a href="#浏览器的渲染机制" class="header-anchor">#</a> 浏览器的渲染机制</h3> <p>我们现在知道一个页面的代码里，主要包括了 HTML、CSS、Javascript 三大块内容，那么浏览器是怎么解析和加载这些内容的呢？</p> <p>一次浏览器的页面渲染过程中，浏览器会解析三种文件：</p> <ul><li><strong>解析 HTML/SVG/XHTML，会生成一个 DOM 结构树</strong></li> <li><strong>解析 CSS，会生成一个 CSS 规则树</strong></li> <li><strong>解析 JS，可通过 DOM API 和 CSS API 来操作 DOM 结构树和 CSS 规则树</strong></li></ul> <p>CSS 规则树与 DOM 结构树结合，最终生成一个 Render 树（即最终呈现的页面，例如其中会移除 DOM 结构树中匹配到 CSS 里面<code>display:none</code>的 DOM 节点）。一般来说浏览器绘制页面的过程是：</p> <ol><li><strong>计算 CSS 规则树。</strong></li> <li><strong>生成 Render 树。</strong></li> <li><strong>计算各个节点的大小/position/z-index。</strong></li> <li><strong>绘制。</strong></li></ol> <p>更多的资料，大家可以参考<a href="https://coolshell.cn/articles/9666.html" target="_blank" rel="noopener noreferrer">《浏览器的渲染原理简介》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，或者英文很棒的你也可以阅读原文<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank" rel="noopener noreferrer">《How browsers work》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <h3 id="页面的局部刷新"><a href="#页面的局部刷新" class="header-anchor">#</a> 页面的局部刷新</h3> <p>一般看来，浏览器生成了最终的 Render 树，页面也已经渲染完毕，似乎浏览器已经完成了它的工作了。但现实中我们的页面更多的不只是静态的页面，还会包括点击、拖拽等事件操作，以及接口请求、数据渲染到页面等动态的交互逻辑，这时候我们会需要更新页面的信息。</p> <p>我们的业务代码中情况会复杂得多，除了插入内容，还包括内容更新、删除元素节点等。不管是那种情况，目前来说前端一般分为两种方式：</p> <ol><li>绑定映射表方式。</li> <li>直接替换内容方式。</li></ol> <h4 id="_1-绑定映射表方式"><a href="#_1-绑定映射表方式" class="header-anchor">#</a> 1. 绑定映射表方式</h4> <p>这其实是挺经常使用的一种方式，例如下面这段代码：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;a&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这里拿到了<code>&lt;xxx id=&quot;a&quot;&gt;</code>的这样一个元素映射，我们在更新内容、处理节点的时候就可以用这个映射来直接操作，如：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 1. 更改元素里面内容</span>
a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">&quot;&lt;p&gt;测试&lt;/p&gt;&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 2. 插入一个&lt;a&gt;元素</span>
a<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">a</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 3. 删除第一个元素，在这里是前面的&lt;p&gt;测试&lt;/p&gt;</span>
a<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>firstChild<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>如果我们一个页面里需要绑定变量的元素很多，那每次要更新某块的页面数据，可能会需要保存很多的元素映射，同时需要调用很多很多的<code>createElement()/appendChild()/removeChild()</code>这类方法，也是个不小的开销。这种情况下，我们可以使用直接替换内容的方式。</p> <h4 id="_2-直接替换内容方式"><a href="#_2-直接替换内容方式" class="header-anchor">#</a> 2. 直接替换内容方式</h4> <p>我们每次更新页面数据和状态，还可以通过<code>innerHTML</code>方法来用新的<code>HTML String</code>替换旧的，这种方法写起来很简单，无非是将各种节点使用字符串的方式拼接起来而已。</p> <p>例如，上面的几次更新 a 元素节点，可以调整成这样实现：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 1. 更改元素里面内容</span>
a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">&quot;&lt;p&gt;测试&lt;/p&gt;&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 2. 插入一个&lt;a&gt;元素</span>
a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">&quot;&lt;p&gt;测试&lt;/p&gt;&lt;a&gt;&lt;/a&gt;&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 3. 删除第一个元素，在这里是前面的&lt;p&gt;测试&lt;/p&gt;</span>
a<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">&quot;&lt;a&gt;&lt;/a&gt;&quot;</span><span class="token punctuation">;</span>
</code></pre></div><p>这种方式来更新页面简单粗暴，但是如果我们更新的节点范围比较大，这时候我们需要替换掉很大一片的<code>HTML String</code>。这种情况下，会面临着可能导致更多的浏览器计算。</p> <h3 id="页面回流、重绘"><a href="#页面回流、重绘" class="header-anchor">#</a> 页面回流、重绘</h3> <p>前面也介绍了，浏览器绘制页面的过程是：1.计算<code>CSS规则树</code> =&gt; 2.生成<code>Render树</code> =&gt; 3.计算各个节点的<code>大小</code>/<code>position</code>/<code>z-index</code> =&gt; 4.绘制。其中计算的环节也是消耗较大的地方。</p> <p>我们使用 DOM API 和 CSS API 的时候，通常会触发浏览器的两种操作：Repaint（重绘） 和 Reflow（回流）：</p> <ul><li><strong>Repaint：页面部分重画，通常不涉及尺寸的改变，常见于颜色的变化。</strong> 这时候一般只触发绘制过程的第 4 个步骤。</li> <li><strong>Reflow：意味着节点需要重新计算和绘制，常见于尺寸的改变。</strong> 这时候会触发 3 和 4 两个步骤。<br>
在 Reflow 的时候，浏览器会使渲染树中受到影响的部分失效，并重新构造这部分渲染树，完成 Reflow 后，浏览器会重新绘制受影响的部分到屏幕中，该过程成为 Repaint。</li></ul> <p>回流的花销跟 render tree 有多少节点需要重新构建有关系，这也是为什么前面说使用<code>innerHTML</code>会导致更多的开销。所以到底是使用绑定映射表方式，还是使用直接替换内容方式，都是需要具体问题具体分析的。</p> <h2 id="事件驱动"><a href="#事件驱动" class="header-anchor">#</a> 事件驱动</h2> <p>事件驱动其实是前端开发中最容易理解的编码方式，例如我们写一个提交表单的页面，用事件驱动的方式来写的话，会是这样一个流程：</p> <ol><li>编写静态页面。</li></ol> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">&gt;</span></span>
  Name:
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name-value<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text<span class="token punctuation">&quot;</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name-input<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
  Email:
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email-value<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email<span class="token punctuation">&quot;</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email<span class="token punctuation">&quot;</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email-input<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><ol start="2"><li>给对应的元素绑定对应的事件。例如给 input 输入框绑定输入事件：</li></ol> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> nameInputEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;name-input&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> emailInputEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;email-input&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 监听输入事件，此时 updateValue 函数未定义</span>
nameInputEl<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;input&quot;</span><span class="token punctuation">,</span> updateNameValue<span class="token punctuation">)</span><span class="token punctuation">;</span>
emailInputEl<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;input&quot;</span><span class="token punctuation">,</span> updateEmailValue<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><ol start="3"><li>事件触发时，更新页面内容：</li></ol> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> nameValueEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;name-value&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> emailValueEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;email-value&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 定义 updateValue 函数，用来更新页面内容</span>
<span class="token keyword">function</span> <span class="token function">updateNameValue</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  nameValueEl<span class="token punctuation">.</span>innerText <span class="token operator">=</span> e<span class="token punctuation">.</span>srcElement<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function">updateEmailValue</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  emailValueEl<span class="token punctuation">.</span>innerText <span class="token operator">=</span> e<span class="token punctuation">.</span>srcElement<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>以上这个流程，是很常见的前端编码思维，我们称之为 <strong>事件驱动</strong> 模式。</p> <h1 id="前端思维转变"><a href="#前端思维转变" class="header-anchor">#</a> 前端思维转变</h1> <p>很多人不理解这几年来前端的变化，为什么不能再用 jQuery 统一天下呢？为什么要搞那么多的库，还要按照环境呢？不是用个 txt 编辑器就能写完一个页面吗，前端弄那么复杂是为了什么呢？</p> <p>既然称之为思维转变，那么可以将事件驱动的思维模式作为过去常见的一种方式，而如今的前端开发过程中，多了很多的新框架、新工具，还有了工程化，这些带来了怎样的思维模式的转变呢？</p> <h2 id="前端框架的出现"><a href="#前端框架的出现" class="header-anchor">#</a> 前端框架的出现</h2> <p>其实最初是 AngularJS 开始占领了比较多的地位，后面 React 迎面赶上，在 Angular 断崖升级的帮助下，Vue 结合了各种框架的优势，以及非常容易入门的文档，成功成为了那一匹黑马。既然这一系列写的是 Vue 的入门和使用，那这里当然是基于 Vue 来介绍了。</p> <h3 id="数据绑定"><a href="#数据绑定" class="header-anchor">#</a> 数据绑定</h3> <h4 id="vue-文本插值"><a href="#vue-文本插值" class="header-anchor">#</a> Vue 文本插值</h4> <p>在 Vue 中，最基础的模板语法是数据绑定，例如：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>{{ data }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>这里绑定了一个 msg 的变量，开发者在 Vue 实例 data 中绑定该变量：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  data<span class="token operator">:</span> <span class="token punctuation">{</span>
    data<span class="token operator">:</span> <span class="token string">&quot;测试文本&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>最终页面展示内容为<code>&lt;div&gt;测试文本&lt;/div&gt;</code>。</p> <h4 id="数据绑定的实现"><a href="#数据绑定的实现" class="header-anchor">#</a> 数据绑定的实现</h4> <p>这种使用双大括号来绑定变量的方式，我们称之为数据绑定。它是怎么实现的呢，数据绑定的过程其实不复杂：</p> <ol><li>解析语法生成 AST。</li> <li>根据 AST 结果生成 DOM。</li> <li>将数据绑定更新至模板。</li></ol> <p>上述这个过程，是模板引擎在做的事情。我们来看看上面在 Vue 里的代码片段<code>&lt;div&gt;&lt;/div&gt;</code>，我们可以解析后获得这样一个 AST 对象：</p> <div class="language-js extra-class"><pre class="language-js"><code>thisDiv <span class="token operator">=</span> <span class="token punctuation">{</span>
  dom<span class="token operator">:</span> <span class="token punctuation">{</span>
    type<span class="token operator">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span>
    ele<span class="token operator">:</span> <span class="token string">&quot;div&quot;</span><span class="token punctuation">,</span>
    nodeIndex<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
    children<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token string">&quot;&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  binding<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">,</span> nodeIndex<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> valueName<span class="token operator">:</span> <span class="token string">&quot;data&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>这样，我们在生成一个 DOM 的时候，同时添加对<code>data</code>的监听，数据更新时我们会找到对应的<code>nodeIndex</code>，更新值：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 假设这是一个生成 DOM 的过程，包括 innerHTML 和事件监听</span>
<span class="token keyword">function</span> <span class="token function">generateDOM</span><span class="token punctuation">(</span><span class="token parameter">astObject</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> dom<span class="token punctuation">,</span> binding <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token operator">=</span> astObject<span class="token punctuation">;</span>
  <span class="token comment">// 生成DOM，这里假装当前节点是baseDom</span>
  baseDom<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span>dom<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 对于数据绑定的，来进行监听更新吧</span>
  baseDom<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;data:change&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 寻找匹配的数据绑定</span>
    <span class="token keyword">const</span> obj <span class="token operator">=</span> binding<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=&gt;</span> x<span class="token punctuation">.</span>valueName <span class="token operator">==</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 若找到值绑定的对应节点，则更新其值。</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      baseDom<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[data-node-index=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>obj<span class="token punctuation">.</span>nodeIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;]</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 获取DOM字符串，这里简单拼成字符串</span>
<span class="token keyword">function</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span><span class="token parameter">domObj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 无效对象返回''</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>domObj<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> type<span class="token punctuation">,</span> children <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> nodeIndex<span class="token punctuation">,</span> ele<span class="token punctuation">,</span> value <span class="token punctuation">}</span> <span class="token operator">=</span> domObj<span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> <span class="token string">&quot;dom&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 若有子对象，递归返回生成的字符串拼接</span>
    <span class="token keyword">const</span> childString <span class="token operator">=</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
    children<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      childString <span class="token operator">+=</span> <span class="token function">getDOMString</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// dom对象，拼接生成对象字符串</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ele<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> data-node-index=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>nodeIndex<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>childString<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>ele<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&gt;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">==</span> <span class="token string">&quot;text&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 若为textNode，返回text的值</span>
    <span class="token keyword">return</span> value<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>关于模板引擎更多的情况，可以查看<a href="/front-end-playground/front-end-basic/understanding/template-engine.html">《前端模板引擎》</a>。</p> <h3 id="虚拟-dom"><a href="#虚拟-dom" class="header-anchor">#</a> 虚拟 DOM</h3> <p>虚拟 DOM 如今已经被作为前端面试基础题了，多多少少面试者都要去了解下，当初 React 就是因为虚拟 DOM 的提出，暂时赢下了浏览器性能之争。当然，这都是几年前的事情了，如今几大框架的性能问题，早也不是什么大问题了。</p> <p>虚拟 DOM 大概是这么个过程：<br>
(1) 用 JS 对象模拟 DOM 树，得到一棵虚拟 DOM 树。<br>
(2) 当页面数据变更时，生成新的虚拟 DOM 树，比较新旧两棵虚拟 DOM 树的差异。<br>
(3) 把差异应用到真正的 DOM 树上。</p> <p>为什么要用到虚拟 DOM 呢？这是因为一个 DOM 节点它包括了太多太多的属性、元素和事件对象，感觉有上百个。但是我们并不是全部都会用到，通常包括节点内容、元素位置、样式、节点的添加删除等方法，而我们通过用 JS 对象表示 DOM 元素的方式，大大降低了比较差异的计算量。</p> <p>虚拟 DOM 中，差异对比也是很关键的一步，这里简单说一下。当状态变更的时候，重新构造一棵新的对象树。然后用新的树和旧的树进行比较，记录两棵树差异。通常来说这样的差异需要记录：需要替换掉原来的节点、移动、删除、新增子节点、修改了节点的属性、 对于文本节点的文本内容改变。经过差异对比之后，我们能获得一组差异记录，接下里我们需要使用它。</p> <p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/wxapp-4.png" alt="DOM 树对比"></p> <p>如图，这里我们对比两棵 DOM 树，得到的差异有：p 元素插入了一个 span 元素子节点，然后原先的文本节点挪到了 span 元素子节点下面，最后通过 JS 操作就可以实现完。</p> <h3 id="xss-漏洞"><a href="#xss-漏洞" class="header-anchor">#</a> XSS 漏洞</h3> <p>模板引擎还可以协助预防下 XSS 相关漏洞。我们知道，XSS 的整个攻击过程大概为：</p> <ol><li>通常页面中包含的用户输入内容都在固定的容器或者属性内，以文本的形式展示。</li> <li>攻击者利用这些页面的用户输入片段，拼接特殊格式的字符串，突破原有位置的限制，形成了代码片段。</li> <li>攻击者通过在目标网站上注入脚本，使之在用户的浏览器上运行，从而引发潜在风险。</li></ol> <p>避免 XSS 的方法之一主要是将用户所提供的内容进行过滤，而大多数模板引擎会自带 HTML 转义功能。在 Vue 中，默认的数据绑定方式（双大括号、<code>v-bind</code>等）会进行 HTML 转义，将数据解释为普通文本，而非 HTML 代码。</p> <p>当然，如果你一定要输出 HTML 代码，也可以使用<code>v-html</code>指令输出。<a href="https://cn.vuejs.org/v2/guide/syntax.html#%E5%8E%9F%E5%A7%8B-HTML" target="_blank" rel="noopener noreferrer">官方文档<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>也有特殊说明：</p> <blockquote><p>你的站点上动态渲染的任意 HTML 可能会非常危险，因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值，绝不要对用户提供的内容使用插值。</p></blockquote> <p>关于 XSS 漏洞，更多的可以查看<a href="https://tech.meituan.com/2018/09/27/fe-security.html" target="_blank" rel="noopener noreferrer">《前端安全系列（一）：如何防止 XSS 攻击？》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>一文。</p> <h3 id="vue-简介"><a href="#vue-简介" class="header-anchor">#</a> Vue 简介</h3> <p>看看这些模板引擎都做了啥事，原本就是一个<code>&lt;div&gt;</code>，经过 AST 生成一个对象，最终还是生成一个<code>&lt;div&gt;</code>，这是多余的步骤吗？不是的，在这个过程中我们可以实现一些功能：</p> <ul><li>排除无效 DOM 元素，并在构建过程可进行报错</li> <li>使用自定义组件的时候，可匹配出来</li> <li>可方便地实现数据绑定、事件绑定等，具备自动更新页面的功能</li> <li>为虚拟 DOM Diff 过程打下铺垫</li> <li>HTML 转义（预防 XSS 漏洞）</li></ul> <p>所以 Vue 它只是一个模板引擎吗？怎么说呢，模板引擎可能是我们选择框架的原因里最重要的一个，毕竟如果没有框架的话，所有上述这些很好用的能力都需要自己搭建，不然开发效率会很受影响。</p> <p>我们看看 Vue 官方的介绍：</p> <blockquote><p>Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是，Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层，不仅易于上手，还便于与第三方库或既有项目整合。</p></blockquote> <p>关于 Vue 和其他框架的对比，可以看看官方文档-<a href="https://cn.vuejs.org/v2/guide/comparison.html" target="_blank" rel="noopener noreferrer">对比其他框架<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。易于上手这块，是大多数人都比较认可的，框架的性能也不错，这也是技术选型中比较重要的一些考虑。</p> <h2 id="数据驱动"><a href="#数据驱动" class="header-anchor">#</a> 数据驱动</h2> <p>前面也介绍了，在 jQuery 年代，我们通常是使用事件驱动的模式去进行开发了。那么使用了 Vue 之后，写代码的方式会有哪些不一样吗？</p> <p>既然前面介绍了事件模型一般的编码流程，我们再来看看，同样的们写一个提交表单的页面，用数据驱动的方式来写的话，会变成这么一个流程：</p> <ol><li>设计数据结构。</li></ol> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 包括一个 name 和 一个 email 的值</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      name<span class="token operator">:</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
      email<span class="token operator">:</span> <span class="token string">&quot;&quot;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><ol start="2"><li>把数据绑定到页面中需要使用/展示的地方。</li></ol> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">&gt;</span></span>
  Name:
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>{{ name }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
    <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name"><span class="token namespace">v-bind:</span>value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>name<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name"><span class="token namespace">v-on:</span>input</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>updateNameValue<span class="token punctuation">&quot;</span></span>
  <span class="token punctuation">/&gt;</span></span>
  Email:
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>{{ email }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
    <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name"><span class="token namespace">v-bind:</span>value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>email<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name"><span class="token namespace">v-on:</span>input</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>updateEmailValue<span class="token punctuation">&quot;</span></span>
  <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><ol start="3"><li>事件触发时，更新数据。</li></ol> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      name<span class="token operator">:</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
      email<span class="token operator">:</span> <span class="token string">&quot;&quot;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  methods<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 绑定 input 事件，获取到输入值，设置到对应的数据中</span>
    <span class="token function">updateNameValue</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token function">updateEmailValue</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>email <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>我们在设置数据（<code>this.name = event.target.value</code>）的时候，Vue 已经替我们把将数据更新到页面的逻辑处理了。大家也可以去 <a href="https://codepen.io/anon/pen/qzPbWQ" target="_blank" rel="noopener noreferrer">codepen<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 或者 jsfiddle、<a href="https://stackblitz.com/" target="_blank" rel="noopener noreferrer">stackblitz<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 这些在线编码网站上尝试下。</p> <p>所以事件驱动和数据驱动一个很重要的区别在于，我们是从每个事件的触发开始设计我们的代码，还是以数据为中心，接收事件触发和更新数据状态的方式来写代码。</p> <h3 id="页面编写"><a href="#页面编写" class="header-anchor">#</a> 页面编写</h3> <p>如果要问，vue 和 jQuery 有什么不一样？其实它们从定位到设计到实现上都完全不一样，但是对开发者来说，我们可以做个简单直观的区别：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!--1. jQuery + 事件驱动--&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text<span class="token punctuation">&quot;</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>input<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>p<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
  <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">&quot;#input&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">&quot;click&quot;</span><span class="token punctuation">,</span> <span class="token parameter">e</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> val <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">;</span>
    <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">&quot;#p&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>

<span class="token comment">&lt;!--2. vue + 数据驱动--&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text<span class="token punctuation">&quot;</span></span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>inputValue<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>{{ inputValue }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>当我们在 Vue 中，模板引擎帮我们处理了模板渲染、数据绑定的过程，我们只需要知道这里面只有一个有效数据，即输入框的值。</p> <h3 id="页面抽象"><a href="#页面抽象" class="header-anchor">#</a> 页面抽象</h3> <p>既然使用了数据驱动，那么对页面的一个抽象能力也是很重要的。例如我们现在要写一个列表，数据从后台获取到之后，展示到页面中。</p> <ol><li>当我们需要渲染成列表时：</li></ol> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!--1). jQuery + 事件驱动--&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>ul<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">&gt;</span></span><span class="token script"><span class="token language-javascript">
  <span class="token keyword">const</span> dom <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">&quot;#ul&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  list<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">item</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    dom<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>
      <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;li data-id=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>item<span class="token punctuation">.</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;&gt;&lt;span&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>item<span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/span&gt;: &lt;a href=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
        item<span class="token punctuation">.</span>href
      <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>item<span class="token punctuation">.</span>href<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/a&gt;&lt;/li&gt;</span><span class="token template-punctuation string">`</span></span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>

<span class="token comment">&lt;!--2). vue + 数据驱动--&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>item in list<span class="token punctuation">&quot;</span></span> <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>item.id<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{item.name}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">:href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>item.href<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>{{item.href}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><ol start="2"><li>当我们需要更新一个列表中某个 id 的其中一个数据时（这里需要更改 id 为 3 的 name 值）：</li></ol> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 1). jQuery + 事件驱动</span>
<span class="token keyword">const</span> dom <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">&quot;#ul&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
dom<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">li[data-id=&quot;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&quot;] span</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token string">&quot;newName3&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 2). vue + 数据驱动</span>
<span class="token keyword">const</span> id <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
list<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token parameter">item</span> <span class="token operator">=&gt;</span> item<span class="token punctuation">.</span>id <span class="token operator">==</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name <span class="token operator">==</span> <span class="token string">&quot;newName3&quot;</span><span class="token punctuation">;</span>
</code></pre></div><p>在使用数据驱动的时候，模板渲染的事情会交给框架去完成，我们需要做的就是数据处理而已。那么转换了数据驱动之后，有什么好处呢？</p> <p>当我们把一个页面抽象成数据来表示之后，可以做的事情就多了。如果我们把所有的页面、组件、展示内容和接口配置等都变成了配置数据，那么我们就可以把所有的东西都进行配置化。以前也写过一篇<a href="/front-end-playground/front-end-basic/understanding/component-with-configuration.html">《组件配置化》</a>，另外在玩 Angular 的时候也做过类似的设计，包括<a href="http://angular2-form-schame.godbasin.com/index.html" target="_blank" rel="noopener noreferrer">组件的配置--通过配置生成表单<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>和<a href="http://angular-custom-app.godbasin.com/#/home/custom-app" target="_blank" rel="noopener noreferrer">页面应用的配置--通过配置生成管理端<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。当然，Vue 也完全可以做这种事情的，配置化的难度常常不在于技术有多难，而在于怎么把业务进行适当的抽象。</p> <h2 id="前端工程化"><a href="#前端工程化" class="header-anchor">#</a> 前端工程化</h2> <p>其实前端工程化这块，我并不是能理解得很深刻，能讲给大家的，应该只是自己的一种理解了。一般来说，现在的主流构建工具应该是 Webpack，包括我们使用 Vue 官方脚手架生成的代码，构建工具也是 Webpack。</p> <p>我们在代码中会使用到很多的资源，图片、样式、代码，还有各式各样的依赖包，而打包的时候怎么实现按需分块、处理依赖关系、不包含多余的文件或内容，同时提供开发和生产环境，包括本地调试自动更新到浏览器这些能力，都是由 Webpack 整合起来的。</p> <h3 id="npm-依赖包"><a href="#npm-依赖包" class="header-anchor">#</a> npm 依赖包</h3> <p>要实现工程化，前端开发几乎都离不开 nodejs 的包管理器 npm，比如前端在搭建本地开发服务以及打包编译前端代码等都会用到。在前端开发过程中，经常用到<code>npm install</code>来安装所需的依赖。</p> <p>为什么会有 npm 包的出现呢？因为一个人的力量是有限的，我们在写代码的时候，为了可以将注意力集中到业务开发中，会需要使用别人开源的很多工具、框架和代码包。在很早以前，我们是一个个地下载，或是通过<code>&lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&gt;&lt;/script&gt;</code>的方式去引用。</p> <p>当一个网站依赖的代码越来越多，其实管理依赖和版本是一件很麻烦的事情。然后 npm 就出现了，作为一个把这些代码集中到一起来管理的工具。</p> <p>同时，我们可以结合一些 tree shaking 的能力，在本地构建的时候，把使用的别人的依赖包里没用用到的部分去掉，减小代码包的大小等。</p> <p>在安装 Node.js 的时候，npm 的能力也会一块提供到，按照完之后就能直接在机器终端使用<code>npm install xxx</code>这种能力啦，需要安装什么依赖包呢，你可以去<a href="https://www.npmjs.com/" target="_blank" rel="noopener noreferrer">npmjs 官网<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>搜一下，也可以自己发布一些包供别人使用，例如之前我也写过一个<a href="https://www.npmjs.com/package/v-select2-component" target="_blank" rel="noopener noreferrer">v-select2-component<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>组件。</p> <h3 id="vue-cli"><a href="#vue-cli" class="header-anchor">#</a> vue cli</h3> <p>通常来说，脚手架可以让你快速地生成示例代码、搭建本地环境，也可以更新依赖的版本等，避免了每个开发者自行调整开发环境、打包逻辑等配置。</p> <p>Vue cli 也提供了这样的能力，更多的可以参考<a href="https://cli.vuejs.org/zh/" target="_blank" rel="noopener noreferrer">官方文档<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。</p> <blockquote><p>Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接，这样你可以专注在撰写应用上，而不必花好几天去纠结配置的问题。</p></blockquote> <p>使用方式很简单：</p> <div class="language-cmd extra-class"><pre class="language-text"><code>// 安装脚手架
npm install -g @vue/cli
// 脚手架生成 vue 项目，同时会自动安装依赖
vue create vue-cli-demo
</code></pre></div><p>生成之后的代码目录是这样的：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-files.jpg" alt=""></p> <h3 id="启动项目"><a href="#启动项目" class="header-anchor">#</a> 启动项目</h3> <p>一般来说，项目会有个 README.md 文件，你能看到一些简单的说明，例如这里生成的：</p> <div class="language-cmd extra-class"><pre class="language-text"><code># Project setup
yarn install

# Compiles and hot-reloads for development
yarn run serve

# Compiles and minifies for production
yarn run build

# Run your tests
yarn run test

# Lints and fixes files
yarn run lint
</code></pre></div><p>yarn 跟 npm 都是差不多的包管理器，区别在于 yarn 在安装时会速度更快（并行、离线等），以及版本统一管理的比较好。但如果你不是有特殊的喜好或者习惯，其实两个都可以用，例如这里的<code>yarn run serve</code>也可以用<code>npm run serve</code>来运行。</p> <p>如果有些习惯不好的项目缺了 README，这种时候要怎么去启动一个项目呢？可以查看<code>package.json</code>文件：</p> <div class="language-json extra-class"><pre class="language-json"><code><span class="token punctuation">{</span>
  <span class="token property">&quot;scripts&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">&quot;serve&quot;</span><span class="token operator">:</span> <span class="token string">&quot;vue-cli-service serve&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;build&quot;</span><span class="token operator">:</span> <span class="token string">&quot;vue-cli-service build&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;lint&quot;</span><span class="token operator">:</span> <span class="token string">&quot;vue-cli-service lint&quot;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>一般来说，开发环境是<code>dev</code>、<code>serve</code>等，生产环境是<code>build</code>，<code>scripts</code>里是一些任务，运行命令<code>npm run taskName</code>就可以启动了。
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-serve.jpg" alt=""></p> <p>上图中，我们可以看到任务已经启动了，访问输出的地址（这里是<code>http://localhost:8080/</code>或者<code>http://10.40.120.53:8080/</code>）就能看到我们的项目跑起来了。
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-cli-demo-page.jpg" alt=""></p> <h2 id="结束语"><a href="#结束语" class="header-anchor">#</a> 结束语</h2> <p>第一 part 结束了，更多的内容还是前端相关。这些主要是这几年对页面渲染的一些新的理解，然后简单用脚手架启动了个 demo 项目。内容是按照自己觉得较清晰的逻辑展开来讲的，如果说你有更好的想法，或是发现我的描述中有哪些不到位的地方，也十分欢迎来交流。<br>
工具始终是工具没错，但一个工具为什么受这么多人追捧，它到底解决了什么，你可以从中学习到什么，这些才是个人认为的在使用工具时候收获的很重要的东西。在 Vue 官方文档很完善的情况下，我来给你们补上文档以外的一些技巧和内容吧。</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/vue/vue-for-everyone/vue-for-everyone-1.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><!----> <span class="next"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html">
          2. 怎么三两下拼出一个页面
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-821" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty2.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.1e2670bf.js" defer></script><script src="/front-end-playground/assets/js/2.38d016d1.js" defer></script><script src="/front-end-playground/assets/js/3.e3f029cb.js" defer></script><script src="/front-end-playground/assets/js/140.f7dc7755.js" defer></script>
  </body>
</html>
